package org.jeecg.modules.demo.gmtreatmentmeasures.controller;

import java.io.File;
import java.io.FileInputStream;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.query.QueryRuleEnum;
import org.jeecg.common.util.MinioUtil;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.demo.gmtreatmentmeasures.entity.GmTreatmentMeasures;
import org.jeecg.modules.demo.gmtreatmentmeasures.service.IGmTreatmentMeasuresService;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;

import org.jeecg.modules.demo.upload.controller.TestExcl;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.jeecg.common.system.base.controller.JeecgController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import com.alibaba.fastjson.JSON;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.apache.shiro.authz.annotation.RequiresPermissions;

 /**
 * @Description: 整治措施
 * @Author: jeecg-boot
 * @Date:   2025-06-23
 * @Version: V1.0
 */
@Api(tags="整治措施")
@RestController
@RequestMapping("/gmtreatmentmeasures/gmTreatmentMeasures")
@Slf4j
public class GmTreatmentMeasuresController extends JeecgController<GmTreatmentMeasures, IGmTreatmentMeasuresService> {
	@Autowired
	private IGmTreatmentMeasuresService gmTreatmentMeasuresService;
	 // 注入自定义线程池
	 @Autowired
	 @Qualifier("gmDataThreadPool")
	 private ThreadPoolTaskExecutor taskExecutor;
	
	/**
	 * 分页列表查询
	 *
	 * @param gmTreatmentMeasures
	 * @param pageNo
	 * @param pageSize
	 * @param req
	 * @return
	 */
	//@AutoLog(value = "整治措施-分页列表查询")
	@ApiOperation(value="整治措施-分页列表查询", notes="整治措施-分页列表查询")
	@GetMapping(value = "/list")
	public Result<IPage<GmTreatmentMeasures>> queryPageList(GmTreatmentMeasures gmTreatmentMeasures,
								   @RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
								   @RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
								   HttpServletRequest req) {
        // 自定义查询规则
        Map<String, QueryRuleEnum> customeRuleMap = new HashMap<>();
        // 自定义多选的查询规则为：LIKE_WITH_OR
        customeRuleMap.put("cutSlopeHeight", QueryRuleEnum.LIKE_WITH_OR);
        customeRuleMap.put("slopeWallDistance", QueryRuleEnum.LIKE_WITH_OR);
        customeRuleMap.put("naturalSlopeGradient", QueryRuleEnum.LIKE_WITH_OR);
        customeRuleMap.put("slopeType", QueryRuleEnum.LIKE_WITH_OR);
        customeRuleMap.put("type1", QueryRuleEnum.LIKE_WITH_OR);
        QueryWrapper<GmTreatmentMeasures> queryWrapper = QueryGenerator.initQueryWrapper(gmTreatmentMeasures, req.getParameterMap(),customeRuleMap);
		Page<GmTreatmentMeasures> page = new Page<GmTreatmentMeasures>(pageNo, pageSize);
		IPage<GmTreatmentMeasures> pageList = gmTreatmentMeasuresService.page(page, queryWrapper);
		return Result.OK(pageList);
	}
	
	/**
	 *   添加
	 *
	 * @param gmTreatmentMeasures
	 * @return
	 */
	@AutoLog(value = "整治措施-添加")
	@ApiOperation(value="整治措施-添加", notes="整治措施-添加")
	@RequiresPermissions("gmtreatmentmeasures:gm_treatment_measures:add")
	@PostMapping(value = "/add")
	public Result<String> add(@RequestBody GmTreatmentMeasures gmTreatmentMeasures) {
		gmTreatmentMeasuresService.save(gmTreatmentMeasures);
		return Result.OK("添加成功！");
	}
	
	/**
	 *  编辑
	 *
	 * @param gmTreatmentMeasures
	 * @return
	 */
	@AutoLog(value = "整治措施-编辑")
	@ApiOperation(value="整治措施-编辑", notes="整治措施-编辑")
	@RequiresPermissions("gmtreatmentmeasures:gm_treatment_measures:edit")
	@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
	public Result<String> edit(@RequestBody GmTreatmentMeasures gmTreatmentMeasures) {
		gmTreatmentMeasuresService.updateById(gmTreatmentMeasures);
		return Result.OK("编辑成功!");
	}
	
	/**
	 *   通过id删除
	 *
	 * @param id
	 * @return
	 */
	@AutoLog(value = "整治措施-通过id删除")
	@ApiOperation(value="整治措施-通过id删除", notes="整治措施-通过id删除")
	@RequiresPermissions("gmtreatmentmeasures:gm_treatment_measures:delete")
	@DeleteMapping(value = "/delete")
	public Result<String> delete(@RequestParam(name="id",required=true) String id) {
		gmTreatmentMeasuresService.removeById(id);
		return Result.OK("删除成功!");
	}
	
	/**
	 *  批量删除
	 *
	 * @param ids
	 * @return
	 */
	@AutoLog(value = "整治措施-批量删除")
	@ApiOperation(value="整治措施-批量删除", notes="整治措施-批量删除")
	@RequiresPermissions("gmtreatmentmeasures:gm_treatment_measures:deleteBatch")
	@DeleteMapping(value = "/deleteBatch")
	public Result<String> deleteBatch(@RequestParam(name="ids",required=true) String ids) {
		this.gmTreatmentMeasuresService.removeByIds(Arrays.asList(ids.split(",")));
		return Result.OK("批量删除成功!");
	}
	
	/**
	 * 通过id查询
	 *
	 * @param id
	 * @return
	 */
	//@AutoLog(value = "整治措施-通过id查询")
	@ApiOperation(value="整治措施-通过id查询", notes="整治措施-通过id查询")
	@GetMapping(value = "/queryById")
	public Result<GmTreatmentMeasures> queryById(@RequestParam(name="id",required=true) String id) {
		GmTreatmentMeasures gmTreatmentMeasures = gmTreatmentMeasuresService.getById(id);
		if(gmTreatmentMeasures==null) {
			return Result.error("未找到对应数据");
		}
		return Result.OK(gmTreatmentMeasures);
	}
	 @RequestMapping(value = "/updateImages2", method = RequestMethod.POST)
	 public Result<?> updateImages2(
			 @RequestParam String folderPath,
			 @RequestParam String imageExt) throws Exception {
		 // 1. 检查目录有效性
		 File folder = new File(folderPath);
		 if (!folder.exists() || !folder.isDirectory()) {
			 return Result.error("文件夹不存在或不是有效目录");
		 }

		 // 2. 获取所有图片文件
		 final String lowerExt = imageExt.toLowerCase();
		 List<File> imageFiles = Arrays.stream(Objects.requireNonNull(folder.listFiles()))
				 .filter(file -> {
					 String name = file.getName().toLowerCase();
					 return name.endsWith(lowerExt) && file.isFile();
				 })
				 .collect(Collectors.toList());

		 if (imageFiles.isEmpty()) {
			 return Result.OK("未找到符合条件的图片文件");
		 }
		 log.info("找到图片文件: " + imageFiles.size() + " 个");

		 // 3. 一次性查询所有整治措施记录
		 log.info("开始查询所有整治措施记录...");
		 long start = System.currentTimeMillis();
		 List<GmTreatmentMeasures> allMeasures = gmTreatmentMeasuresService.list();
		 long end = System.currentTimeMillis();
		 log.info("查询完成，共 " + allMeasures.size() + " 条记录，耗时 " + (end - start) + "ms");

		 // 4. 构建code->实体的映射
		 Map<String, GmTreatmentMeasures> codeToMeasureMap = allMeasures.stream()
				 .collect(Collectors.toMap(GmTreatmentMeasures::getCode, Function.identity()));

		 log.info("构建编码映射表，共 " + codeToMeasureMap.size() + " 个唯一编码");

		 // 5. 正则表达式用于提取编码
		 Pattern pattern = Pattern.compile("([A-Z]{3}-\\d+-\\d+)");

		 // 6. 准备数据结构
		 Map<String, File> validFiles = new HashMap<>(); // 有效编码 -> 文件
		 Set<String> invalidFiles = new HashSet<>();     // 无效文件名
		 Set<String> missingRecords = new HashSet<>();   // 无对应记录的编码

		 // 7. 提取有效编码
		 for (File imageFile : imageFiles) {
			 String fileName = imageFile.getName();
			 Matcher matcher = pattern.matcher(fileName);

			 if (matcher.find()) {
				 String fullCode = matcher.group(1);
				 // 只保留第一个匹配的文件
				 if (!validFiles.containsKey(fullCode)) {
					 validFiles.put(fullCode, imageFile);
					 log.info("匹配: " + fullCode + " -> " + fileName);
				 }
			 } else {
				 invalidFiles.add(fileName);
				 log.info("无效文件名: " + fileName);
			 }
		 }
		 List<GmTreatmentMeasures> toUpdate = Collections.synchronizedList(new ArrayList<>());
		 Set<String> uploadFailed = ConcurrentHashMap.newKeySet();
		 // 8. 使用Spring线程池执行任务
		 List<CompletableFuture<Void>> futures = new ArrayList<>();

		 for (Map.Entry<String, File> entry : validFiles.entrySet()) {
			 // 使用CompletableFuture提交任务
			 CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
				 String code = entry.getKey();
				 File imageFile = entry.getValue();
				 // 查找对应的整治措施记录
				 GmTreatmentMeasures measure = codeToMeasureMap.get(code);
				 if (measure == null) {
					 System.out.println("警告: 无匹配记录 - " + code);
					 return;
				 }

				 // 检查是否已有图片
				 if (measure.getImage() != null && !measure.getImage().isEmpty()) {
					 System.out.println("跳过: 已有图片 - " + code);
					 return;
				 }

				 try {
					 // 上传图片
					 System.out.println("开始上传: " + code);
					 String url = MinioUtil.upload(imageFile, "");
					 System.out.println("上传成功: " + code + " -> " + url);

					 // 设置图片URL并添加到更新列表
					 measure.setImage(url);
					 toUpdate.add(measure);

				 } catch (Exception e) {
					 System.err.println("上传失败: " + code + " - " + e.getMessage());
				 }
			 }, taskExecutor); // 使用Spring管理的线程池

			 futures.add(future);
		 }

		 // 9. 等待所有任务完成（关键！）
		 try {
			 CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).get(10, TimeUnit.MINUTES);
			 System.out.println("所有上传任务完成");
		 } catch (Exception e) {
			 System.err.println("任务等待超时或异常: " + e.getMessage());
		 }
		 // 10. 构建结果统计
		 StringBuilder result = new StringBuilder("图集更新完成！统计信息:\n");
		 result.append("• 处理文件: ").append(imageFiles.size()).append(" 个\n");
		 result.append("• 有效编码: ").append(validFiles.size()).append(" 个\n");
		 result.append("• 上传成功: ").append(toUpdate.size()).append(" 个\n");
		 result.append("• 更新记录: ").append(toUpdate.size()).append(" 条\n");

		 if (!invalidFiles.isEmpty()) {
			 result.append("• 无效文件: ").append(invalidFiles.size())
					 .append(" 个 (示例: ").append(invalidFiles.stream().limit(5).collect(Collectors.joining(", ")))
					 .append(")\n");
		 }

		 if (!uploadFailed.isEmpty()) {
			 result.append("• 上传失败: ").append(uploadFailed.size())
					 .append(" 个 (示例: ").append(uploadFailed.stream().limit(5).collect(Collectors.joining(", ")))
					 .append(")\n");
		 }

		 if (!missingRecords.isEmpty()) {
			 result.append("• 无对应记录: ").append(missingRecords.size())
					 .append(" 个 (示例: ").append(missingRecords.stream().limit(5).collect(Collectors.joining(", ")))
					 .append(")\n");
		 }
		 log.info(result.toString());

		 return Result.OK(result.toString());

		 // ... [后续代码保持不变] ...
	 }


	 @RequestMapping(value = "/updateImages1", method = RequestMethod.POST)
	 @ApiOperation(value = "上传剖面图并更新整治措施", notes = "上传剖面图并更新整治措施")
	 public Result<?> updateImages1(
			 @RequestParam String folderPath,
			 @RequestParam String imageExt) {

		 // 1. 检查目录有效性
		 File folder = new File(folderPath);
		 if (!folder.exists() || !folder.isDirectory()) {
			 return Result.error("文件夹不存在或不是有效目录");
		 }

		 // 2. 获取所有图片文件
		 final String lowerExt = imageExt.toLowerCase();
		 List<File> imageFiles = Arrays.stream(Objects.requireNonNull(folder.listFiles()))
				 .filter(file -> {
					 String name = file.getName().toLowerCase();
					 return name.endsWith(lowerExt) && file.isFile();
				 })
				 .collect(Collectors.toList());

		 if (imageFiles.isEmpty()) {
			 return Result.OK("未找到符合条件的图片文件");
		 }
		 log.info("找到图片文件: " + imageFiles.size() + " 个");

		 // 3. 一次性查询所有整治措施记录
		 log.info("开始查询所有整治措施记录...");
		 long start = System.currentTimeMillis();
		 List<GmTreatmentMeasures> allMeasures = gmTreatmentMeasuresService.list();
		 long end = System.currentTimeMillis();
		 log.info("查询完成，共 " + allMeasures.size() + " 条记录，耗时 " + (end - start) + "ms");

		 // 4. 构建code->实体的映射
		 Map<String, GmTreatmentMeasures> codeToMeasureMap = allMeasures.stream()
				 .collect(Collectors.toMap(GmTreatmentMeasures::getCode, Function.identity()));

		 log.info("构建编码映射表，共 " + codeToMeasureMap.size() + " 个唯一编码");

		 // 5. 正则表达式用于提取编码
		 Pattern pattern = Pattern.compile("([A-Z]{3}-\\d+-\\d+)");

		 // 6. 准备数据结构
		 Map<String, File> validFiles = new HashMap<>(); // 有效编码 -> 文件
		 Set<String> invalidFiles = new HashSet<>();     // 无效文件名
		 Set<String> missingRecords = new HashSet<>();   // 无对应记录的编码

		 // 7. 提取有效编码
		 for (File imageFile : imageFiles) {
			 String fileName = imageFile.getName();
			 Matcher matcher = pattern.matcher(fileName);

			 if (matcher.find()) {
				 String fullCode = matcher.group(1);
				 // 只保留第一个匹配的文件
				 if (!validFiles.containsKey(fullCode)) {
					 validFiles.put(fullCode, imageFile);
					 log.info("匹配: " + fullCode + " -> " + fileName);
				 }
			 } else {
				 invalidFiles.add(fileName);
				 log.info("无效文件名: " + fileName);
			 }
		 }

		 // 8. 并行处理上传和更新
		 List<GmTreatmentMeasures> toUpdate = Collections.synchronizedList(new ArrayList<>());
		 Set<String> uploadFailed = ConcurrentHashMap.newKeySet();

		 // 创建线程池执行上传任务
		 int threadCount = Math.max(4, Runtime.getRuntime().availableProcessors() * 2);
		 ExecutorService executor = Executors.newFixedThreadPool(threadCount);
		 List<Future<?>> futures = new ArrayList<>();

//		 log.info("开始并行处理 " + validFiles.size() + " 个有效文件，使用 " + threadCount + " 线程");

		 for (Map.Entry<String, File> entry : validFiles.entrySet()) {
			 futures.add(executor.submit(() -> {
				 String code = entry.getKey();
				 File imageFile = entry.getValue();

				 // 查找对应的整治措施记录
				 GmTreatmentMeasures measure = codeToMeasureMap.get(code);
				 if (measure == null) {
					 log.info("警告: 无匹配记录 - " + code);
					 missingRecords.add(code);
					 return;
				 }

				 // 检查是否已有图片
				 if (measure.getImage() != null && !measure.getImage().isEmpty()) {
					log.info("跳过: 已有图片 - " + code);
					 return;
				 }

				 try {
					 // 上传图片
					log.info("开始上传: " + code);
					 String url = MinioUtil.upload(imageFile, "");
					 log.info("上传成功: " + code + " -> " + url);

					 // 设置图片URL并添加到更新列表
					 measure.setImage(url);
					 toUpdate.add(measure);

				 } catch (Exception e) {
					 System.err.println("上传失败: " + code + " - " + e.getMessage());
					 uploadFailed.add(code);
				 }
			 }));
		 }

		 // 等待所有上传任务完成
		 try {
			 for (Future<?> future : futures) {
				 future.get(10, TimeUnit.MINUTES); // 设置超时时间
			 }
		 } catch (Exception e) {
			 System.err.println("任务执行异常: " + e.getMessage());
		 } finally {
			 executor.shutdown();
			 System.out.println("所有上传任务完成");
		 }

		 // 9. 批量更新数据库
		 if (!toUpdate.isEmpty()) {
			 log.info("开始批量更新数据库，记录数: " + toUpdate.size());
			 long startUpdate = System.currentTimeMillis();
			 gmTreatmentMeasuresService.updateBatchById(toUpdate);
			 long endUpdate = System.currentTimeMillis();
			 log.info("数据库更新完成，耗时 " + (endUpdate - startUpdate) + "ms");
		 } else {
			 log.info("没有需要更新的记录");
		 }

		 // 10. 构建结果统计
		 StringBuilder result = new StringBuilder("图集更新完成！统计信息:\n");
		 result.append("• 处理文件: ").append(imageFiles.size()).append(" 个\n");
		 result.append("• 有效编码: ").append(validFiles.size()).append(" 个\n");
		 result.append("• 上传成功: ").append(toUpdate.size()).append(" 个\n");
		 result.append("• 更新记录: ").append(toUpdate.size()).append(" 条\n");

		 if (!invalidFiles.isEmpty()) {
			 result.append("• 无效文件: ").append(invalidFiles.size())
					 .append(" 个 (示例: ").append(invalidFiles.stream().limit(5).collect(Collectors.joining(", ")))
					 .append(")\n");
		 }

		 if (!uploadFailed.isEmpty()) {
			 result.append("• 上传失败: ").append(uploadFailed.size())
					 .append(" 个 (示例: ").append(uploadFailed.stream().limit(5).collect(Collectors.joining(", ")))
					 .append(")\n");
		 }

		 if (!missingRecords.isEmpty()) {
			 result.append("• 无对应记录: ").append(missingRecords.size())
					 .append(" 个 (示例: ").append(missingRecords.stream().limit(5).collect(Collectors.joining(", ")))
					 .append(")\n");
		 }
		 log.info(result.toString());

		 return Result.OK(result.toString());
	 }



	 /**
	  * 接口说明：批量上传图片并关联数据库中的整治措施记录
	  */
	 @RequestMapping(value = "/updateImages", method = RequestMethod.POST)
	 @ApiOperation(value = "上传剖面图并更新整治措施", notes = "上传剖面图并更新整治措施")
	 public Result<?> updateImages(
			 @RequestParam String folderPath,
			 @RequestParam String imageExt) throws Exception {
		 // 1. 创建文件对象并检查目录是否存在
		 File folder = new File(folderPath);
		 if (!folder.exists() || !folder.isDirectory()) {
			 return Result.error("文件夹不存在或不是有效目录");
		 }
		 // 2. 获取所有符合后缀的图片文件
		 List<File> imageFiles = Arrays.stream(Objects.requireNonNull(folder.listFiles()))
				 .filter(file -> file.getName().endsWith(imageExt))
				 .collect(Collectors.toList());
		 // 3. 正则表达式用于提取图片名中的编码 AAA-2-1
		 Pattern pattern = Pattern.compile("([A-Z]{3}-\\d+-\\d+)");
		 System.out.println("imageFiles.size() = " + imageFiles.size());
		 // 4. 遍历所有图片文件
		 for (File imageFile : imageFiles) {
			 String fileName = imageFile.getName();
			 Matcher matcher = pattern.matcher(fileName);
			 if (!matcher.find()) {
				 System.out.println("无法解析图片名：" + fileName);
				 continue;
			 }
			 String fullCode = matcher.group(1); // 得到 AAA-2-1
			 System.out.println("匹配到编码：" + fullCode);
			 // 5. 查询数据库中 measure 字段包含该编码的记录
			 QueryWrapper<GmTreatmentMeasures> queryWrapper = new QueryWrapper<>();
			 queryWrapper.eq("code", fullCode).last("limit 1"); // 假设 detail 字段存储原始措施内容
			 GmTreatmentMeasures measure = gmTreatmentMeasuresService.getOne(queryWrapper);
			 if (oConvertUtils.isEmpty(measure)) {
				 System.out.println("未找到与编码 " + fullCode + " 匹配的措施");
				 continue;
			 }
			 if(measure.getImage() != null){
				 continue;
			 }
			 // 6. 上传图片并获取 URL
			 String url = MinioUtil.upload(imageFile,""); // 替换为你自己的上传逻辑
			 // 7. 更新每条匹配记录的图集字段
			 measure.setImage(url); // 设置图集地址字段

			 gmTreatmentMeasuresService.updateById(measure);

		 }

		 return Result.OK("图集更新完成！");
	 }


	 @RequestMapping(value = "/updateImagesMutilThread", method = RequestMethod.POST)
	 @ApiOperation(value = "上传剖面图并更新整治措施", notes = "上传剖面图并更新整治措施")
	 public Result<?> updateImagesMutilThread(
			 @RequestParam String folderPath,
			 @RequestParam String imageExt) {

		 // 1. 检查目录有效性
		 File folder = new File(folderPath);
		 if (!folder.exists() || !folder.isDirectory()) {
			 return Result.error("文件夹不存在或不是有效目录");
		 }

		 // 2. 获取图片文件 (修复: 使用忽略大小写的后缀匹配)
		 String finalExt = imageExt.toLowerCase();
		 List<File> imageFiles = Arrays.stream(Objects.requireNonNull(folder.listFiles()))
				 .filter(file -> file.getName().toLowerCase().endsWith(finalExt))
				 .collect(Collectors.toList());

		 if (imageFiles.isEmpty()) {
			 return Result.OK("未找到符合条件的图片文件");
		 }

		 // 3. 优化正则表达式：允许文件名中额外字符
		 Pattern pattern = Pattern.compile("([A-Z]{3}-\\d+-\\d+)"); // 保持不变但需注意日志中的文件名格式

		 // 4. 线程安全容器
		 Map<String, String> codeToUrlMap = new ConcurrentHashMap<>();
		 Set<String> invalidFiles = ConcurrentHashMap.newKeySet();
		 Set<String> uploadFailedFiles = ConcurrentHashMap.newKeySet(); // 新增：上传失败文件

		 // 5. 使用CompletableFuture替代CountDownLatch
		 List<CompletableFuture<Void>> futures = imageFiles.stream()
				 .map(imageFile -> CompletableFuture.runAsync(() -> {
                     try {
                         processImage(imageFile, pattern, codeToUrlMap, invalidFiles, uploadFailedFiles);
                     } catch (Exception e) {
                         throw new RuntimeException(e);
                     }
                 }, taskExecutor))
				 .collect(Collectors.toList());
       log.info("分批上传图片 " );
		 // 6. 等待所有任务完成（带超时）
		 try {
			 CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
					 .get(10, TimeUnit.MINUTES);
		 } catch (InterruptedException e) {
			 Thread.currentThread().interrupt();
			 return Result.error("处理被中断");
		 } catch (TimeoutException e) {
			 return Result.error("处理超时，部分文件可能未完成");
		 } catch (ExecutionException e) {
			 return Result.error("处理过程中发生错误: " + e.getCause().getMessage());
		 }

		 // 7. 检查有效结果
		 if (codeToUrlMap.isEmpty()) {
			 return Result.OK("没有需要更新的图片，无效/失败文件: " + invalidFiles.size());
		 }
       log.info("分批查询数据库 " );
		 // 8. 分批查询数据库（修复：处理null字段）
		 List<String> codes = new ArrayList<>(codeToUrlMap.keySet());
		 int batchSize = 500;
		 List<GmTreatmentMeasures> allMeasures = new ArrayList<>();

		 for (int i = 0; i < codes.size(); i += batchSize) {
			 List<String> batchCodes = codes.subList(i, Math.min(i + batchSize, codes.size()));
			 List<GmTreatmentMeasures> batchMeasures = gmTreatmentMeasuresService.list(
					 new QueryWrapper<GmTreatmentMeasures>().in("code", batchCodes)
			 );
			 allMeasures.addAll(batchMeasures);
		 }

		 Map<String, GmTreatmentMeasures> measureMap = allMeasures.stream()
				 .collect(Collectors.toMap(GmTreatmentMeasures::getCode, m -> m));

		 // 9. 准备更新（修复：空字段检查）
		 List<GmTreatmentMeasures> toUpdate = new ArrayList<>();
		 Set<String> missingRecords = ConcurrentHashMap.newKeySet();
		 Set<String> skippedRecords = ConcurrentHashMap.newKeySet();

		 for (Map.Entry<String, String> entry : codeToUrlMap.entrySet()) {
			 String code = entry.getKey();
			 String url = entry.getValue();

			 GmTreatmentMeasures measure = measureMap.get(code);
			 if (measure == null) {
				 missingRecords.add(code);
				 continue;
			 }

			 // 修复：安全处理null字段
			 if (measure.getImage() != null && !measure.getImage().isEmpty()) {
				 skippedRecords.add(code);
				 continue;
			 }

			 measure.setImage(url);
			 toUpdate.add(measure);
		 }

		 // 10. 批量更新
		 if (!toUpdate.isEmpty()) {
			 gmTreatmentMeasuresService.updateBatchById(toUpdate);
		 }

		 // 11. 构建结果报告（新增上传失败统计）
		 StringBuilder resultMsg = new StringBuilder("图集更新完成！统计信息:\n");
		 resultMsg.append("• 成功上传图片: ").append(codeToUrlMap.size() - uploadFailedFiles.size()).append(" 张\n");
		 resultMsg.append("• 成功更新记录: ").append(toUpdate.size()).append(" 条\n");

		 if (!uploadFailedFiles.isEmpty()) {
			 resultMsg.append("• 上传失败文件: ").append(uploadFailedFiles.size())
					 .append(" 个 (示例: ").append(uploadFailedFiles.stream().limit(5).collect(Collectors.joining(", ")))
					 .append(")\n");
		 }
		 // ... 其他统计保持原样 ...

		 return Result.OK(resultMsg.toString());
	 }

	 // 优化后的图片处理方法
	 private void processImage(File imageFile, Pattern pattern,
							   Map<String, String> codeToUrlMap,
							   Set<String> invalidFiles,
							   Set<String> uploadFailedFiles) throws Exception {
		 String fileName = imageFile.getName();
//		 Matcher matcher = pattern.matcher(fileName);
//
//
//		 if (!matcher.find()) {
//			 invalidFiles.add(fileName);
//			 return;
//		 }

//		 String fullCode = matcher.group(1);
		 try {
			 String url = MinioUtil.upload3(imageFile, "");
			 // 防止编码冲突：相同编码只保留第一个
//			 codeToUrlMap.putIfAbsent(fullCode, url);
			 log.info("上传成功: " + fileName + " -> " + url);
		 } catch (Exception e) {
			 uploadFailedFiles.add(fileName); // 记录失败文件
			 System.err.println("上传失败: " + fileName + ", 原因: " + e.getMessage());
		 }
	 }


	 @RequestMapping(value = "/updateImagesMutilThread1", method = RequestMethod.POST)
	 @ApiOperation(value = "上传剖面图并更新整治措施", notes = "上传剖面图并更新整治措施")
	 public Result<?> updateImagesMutilThread1(
			 @RequestParam String folderPath,
			 @RequestParam String imageExt) {

		 // 1. 创建文件对象并检查目录
		 File folder = new File(folderPath);
		 if (!folder.exists() || !folder.isDirectory()) {
			 return Result.error("文件夹不存在或不是有效目录");
		 }

		 // 2. 获取所有符合后缀的图片文件
		 List<File> imageFiles = Arrays.stream(Objects.requireNonNull(folder.listFiles()))
				 .filter(file -> file.getName().endsWith(imageExt))
				 .collect(Collectors.toList());

		 // 3. 创建线程安全的数据容器
		 Map<String, String> codeToUrlMap = new ConcurrentHashMap<>(); // 存储编码到URL的映射
		 Set<String> invalidFiles = ConcurrentHashMap.newKeySet(); // 无效文件名
		 Set<String> missingRecords = ConcurrentHashMap.newKeySet(); // 缺失的记录
		 Set<String> skippedRecords = ConcurrentHashMap.newKeySet(); // 已存在图片的记录

		 // 4. 正则表达式用于提取图片名中的编码
		 Pattern pattern = Pattern.compile("([A-Z]{3}-\\d+-\\d+)");

		 // 5. 创建CountDownLatch同步任务
		 CountDownLatch latch = new CountDownLatch(imageFiles.size());

		 // 6. 使用线程池并行处理图片上传
		 for (File imageFile : imageFiles) {
			 taskExecutor.execute(() -> {
				 try {
					 processImage(imageFile, pattern, codeToUrlMap, invalidFiles);
				 } catch (Exception e) {
					 System.err.println("处理图片失败：" + imageFile.getName() + ", 原因: " + e.getMessage());
				 } finally {
					 latch.countDown(); // 任务完成计数减一
				 }
			 });
		 }

		 // 7. 等待所有上传任务完成
		 try {
			 latch.await(10, TimeUnit.MINUTES); // 最多等待10分钟
		 } catch (InterruptedException e) {
			 Thread.currentThread().interrupt();
			 return Result.error("处理被中断");
		 }

		 // 8. 查询数据库并准备批量更新
		 if (codeToUrlMap.isEmpty()) {
			 return Result.OK("没有需要更新的图片");
		 }

//		 // 9. 批量查询数据库记录
//		 List<GmTreatmentMeasures> measures = gmTreatmentMeasuresService.list(
//				 new QueryWrapper<GmTreatmentMeasures>()
//						 .in("code", codeToUrlMap.keySet())
//		 );
 //		  Map<String, GmTreatmentMeasures> measureMap = measures.stream()
//				.collect(Collectors.toMap(GmTreatmentMeasures::getCode, m -> m));

		 // 9分批查询数据库记录（解决数据库 IN 子句参数限制）
		 List<String> codes = new ArrayList<>(codeToUrlMap.keySet());
		 int batchSize = 500; // 每批查询的数量（根据数据库调整）
		 List<GmTreatmentMeasures> allMeasures = new ArrayList<>();
		 for (int i = 0; i < codes.size(); i += batchSize) {
			 int end = Math.min(i + batchSize, codes.size());
			 List<String> batchCodes = codes.subList(i, end);

			 // 执行批量查询
			 List<GmTreatmentMeasures> batchMeasures = gmTreatmentMeasuresService.list(
					 new QueryWrapper<GmTreatmentMeasures>().in("code", batchCodes)
			 );
			 allMeasures.addAll(batchMeasures);
		 }
       // 转换为Map以便快速查找
		 Map<String, GmTreatmentMeasures> measureMap = allMeasures.stream()
				 .collect(Collectors.toMap(GmTreatmentMeasures::getCode, m -> m));


		 // 10. 准备需要更新的实体列表
		 List<GmTreatmentMeasures> toUpdate = new ArrayList<>();


		 // 11. 收集需要更新的记录
		 for (Map.Entry<String, String> entry : codeToUrlMap.entrySet()) {
			 String code = entry.getKey();
			 String url = entry.getValue();

			 GmTreatmentMeasures measure = measureMap.get(code);
			 if (measure == null) {
				 missingRecords.add(code);
				 continue;
			 }

			 if ( !measure.getImage().isEmpty()) {
				 skippedRecords.add(code);
				 continue;
			 }

			 measure.setImage(url);
			 toUpdate.add(measure);
		 }

		 // 12. 批量更新数据库
		 if (!toUpdate.isEmpty()) {
			 gmTreatmentMeasuresService.updateBatchById(toUpdate);
		 }

		 // 13. 构建详细结果报告
		 StringBuilder resultMsg = new StringBuilder("图集更新完成！统计信息:\n");
		 resultMsg.append("• 成功上传图片: ").append(codeToUrlMap.size()).append(" 张\n");
		 resultMsg.append("• 成功更新记录: ").append(toUpdate.size()).append(" 条\n");

		 if (!skippedRecords.isEmpty()) {
			 resultMsg.append("• 跳过已存在图片的记录: ").append(skippedRecords.size())
					 .append(" 条 (示例: ").append(skippedRecords.stream().limit(5).collect(Collectors.joining(", ")))
					 .append(")\n");
		 }

		 if (!missingRecords.isEmpty()) {
			 resultMsg.append("• 缺失数据库记录: ").append(missingRecords.size())
					 .append(" 条 (示例: ").append(missingRecords.stream().limit(5).collect(Collectors.joining(", ")))
					 .append(")\n");
		 }

		 if (!invalidFiles.isEmpty()) {
			 resultMsg.append("• 无效文件名: ").append(invalidFiles.size())
					 .append(" 个 (示例: ").append(invalidFiles.stream().limit(5).collect(Collectors.joining(", ")))
					 .append(")\n");
		 }
		 System.out.println( resultMsg);

		 return Result.OK("上传成功");
	 }

	 // 处理单个图片的方法（只负责上传和提取编码）
	 private void processImage(File imageFile, Pattern pattern,
							   Map<String, String> codeToUrlMap, Set<String> invalidFiles) {
		 String fileName = imageFile.getName();
		 Matcher matcher = pattern.matcher(fileName);

		 if (!matcher.find()) {
			 invalidFiles.add(fileName);
			 return;
		 }

		 String fullCode = matcher.group(1);
		 System.out.println(Thread.currentThread().getName() + " 处理图片: " + fileName);

		 try {
			 // 上传图片到MinIO
			 String url = MinioUtil.upload3(imageFile, "");

			 // 存储编码到URL的映射
			 codeToUrlMap.put(fullCode, url);
		 } catch (Exception e) {
			 System.err.println("上传图片失败: " + fileName + ", 原因: " + e.getMessage());
		 }
	 }





    /**
    * 导出excel
    *
    * @param request
    * @param gmTreatmentMeasures
    */
    @RequiresPermissions("gmtreatmentmeasures:gm_treatment_measures:exportXls")
    @RequestMapping(value = "/exportXls")
    public ModelAndView exportXls(HttpServletRequest request, GmTreatmentMeasures gmTreatmentMeasures) {
        return super.exportXls(request, gmTreatmentMeasures, GmTreatmentMeasures.class, "整治措施");
    }

    /**
      * 通过excel导入数据
    *
    * @param request
    * @param response
    * @return
    */
    @RequiresPermissions("gmtreatmentmeasures:gm_treatment_measures:importExcel")
    @RequestMapping(value = "/importExcel", method = RequestMethod.POST)
    public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
        return super.importExcel(request, response, GmTreatmentMeasures.class);
    }
	 private static final Map<Integer, String> TYPE_MAP = Map.of(
			 1, "A", 2, "A", 3, "B", 4, "C", 5, "C"
	 );
	 // 支持中英文括号，提取类似 AAA-2-1 的编码格式
	 private  Pattern pattern = Pattern.compile("[（(]([A-Z]{3}-\\d+-\\d+)[）)]");
	 /**
	  * 通过excel导入数据
	  *
	  * @param request
	  * @param response
	  * @return
	  */
//	 @RequiresPermissions("gmtreatmentmeasures:gm_treatment_measures:importExcel")
	 @RequestMapping(value = "/handlerExcel", method = RequestMethod.POST)
	 @ApiOperation(value="整治措施-导入Excel", notes="整治措施-导入Excel")
	 public Result<?> handlerExcel(HttpServletRequest request, HttpServletResponse response) throws IOException {
		 List<GmTreatmentMeasures> dataList = new ArrayList<>();

		 MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
		 Map<String, MultipartFile> fileMap = multipartRequest.getFileMap();
		 for (Map.Entry<String, MultipartFile> entity : fileMap.entrySet()) {
			 // 获取上传文件对象
			  MultipartFile file = entity.getValue();
			 try (Workbook workbook = new XSSFWorkbook(file.getInputStream())) {
				 Sheet sheet = workbook.getSheetAt(0); // 使用第一个 sheet
				 for (Row row : sheet) {
					 Cell cell = row.getCell(0); // 只读取第1列
					 if (cell == null) continue;

					 String content = getCellValue(cell);
					 if (content.isEmpty()) continue;

					 // 如果有多个措施，用换行符分割
					 String[] measures = content.split("\n");
					 for (String measure : measures) {
						 measure = measure.trim();
						 if (measure.isEmpty()) continue;

						 Matcher matcher = pattern.matcher(measure);
						 if (!matcher.find()) {
							 System.out.println("无法解析措施：" + measure);
							 continue;
						 }
						 String fullCode = matcher.group(1); // 得到 AAA-2-1
						 String[] parts = fullCode.split("-");
						 String codePart = parts[0]; // AAA
						 int typeNum = Integer.parseInt(parts[1]); // 2
						 int num = Integer.parseInt(parts[2]); // 1
						 String heightCode = codePart.substring(0, 1);
						 String distanceCode = codePart.substring(1, 2);
						 String slopeCode = codePart.substring(2, 3);
						 GmTreatmentMeasures data = new GmTreatmentMeasures();
						 data.setCutSlopeHeight(heightCode) ;
						 data.setSlopeWallDistance(distanceCode) ;
						 data.setNaturalSlopeGradient(slopeCode);
						 data.setSlopeType(TYPE_MAP.getOrDefault(typeNum, "未知"))  ;
						 data.setType1(String.valueOf(typeNum)) ;
						 data.setNum(num);
						 data.setDetail(measure) ; // 原始措施字符串
						 dataList.add(data);
					 }
				 }
			 }
		 }
		 this.gmTreatmentMeasuresService.saveBatch(dataList);
		 return Result.OK("导入成功！");
	 }
	 /**
	  * 通过excel导入数据
	  *
	  * @return
	  */
//	 @RequiresPermissions("gmtreatmentmeasures:gm_treatment_measures:importExcel")
	 @RequestMapping(value = "/handlerExcelFile", method = RequestMethod.POST)
	 @ApiOperation(value="整治措施-导入Excel", notes="整治措施-导入Excel")
	 public Result<?> handlerExcelFile(MultipartFile  file) throws IOException {
		 List<GmTreatmentMeasures> dataList = new ArrayList<>();
			 try (Workbook workbook = new XSSFWorkbook(file.getInputStream())) {
				 Sheet sheet = workbook.getSheetAt(0); // 使用第一个 sheet
				 for (Row row : sheet) {
					 Cell cell = row.getCell(0); // 只读取第1列
					 if (cell == null) continue;

					 String content = getCellValue(cell);
					 if (content.isEmpty()) continue;

					 // 如果有多个措施，用换行符分割
					 String[] measures = content.split("\n");
					 for (String measure : measures) {
						 measure = measure.trim();
						 if (measure.isEmpty()) continue;

						 Matcher matcher = pattern.matcher(measure);
						 if (!matcher.find()) {
							 System.out.println("无法解析措施：" + measure);
							 continue;
						 }
						 String fullCode = matcher.group(1); // 得到 AAA-2-1
						 String[] parts = fullCode.split("-");
						 String codePart = parts[0]; // AAA
						 int typeNum = Integer.parseInt(parts[1]); // 2
						 int num = Integer.parseInt(parts[2]); // 1
						 String heightCode = codePart.substring(0, 1);
						 String distanceCode = codePart.substring(1, 2);
						 String slopeCode = codePart.substring(2, 3);
						 GmTreatmentMeasures data = new GmTreatmentMeasures();
						 data.setCutSlopeHeight(heightCode) ;
						 data.setSlopeWallDistance(distanceCode) ;
						 data.setNaturalSlopeGradient(slopeCode);
						 data.setSlopeType(TYPE_MAP.getOrDefault(typeNum, "未知"))  ;
						 data.setType1(String.valueOf(typeNum)) ;
						 data.setNum(num);
						 data.setDetail(measure) ; // 原始措施字符串
						 data.setCode(fullCode);
						 dataList.add(data);
					 }
				 }
			 }
		 this.gmTreatmentMeasuresService.saveBatch(dataList);
		 return Result.OK("导入成功！");
	 }
	 /** 安全获取单元格值 */
	 private static String getCellValue(Cell cell) {
		 if (cell == null) {
			 return "";
		 }
		 if (cell.getCellType() == CellType.STRING) {
			 return cell.getStringCellValue().trim();
		 } else if (cell.getCellType() == CellType.NUMERIC) {
			 return String.valueOf((int) cell.getNumericCellValue()).trim();
		 } else {
			 return "";
		 }
	 }


}
